#include "swpactivitygraphicitem.h"

#include "../../documents/data/graphicdata.h"
#include "../../undo/basicitemcommands.h"
#include "../../utils/processdocumentautocomplete.h"
#include "../../utils/undomanager.h"
#include "../../utils/validatormanager.h"
#include "graphicitemcommon.h"
#include "swpactivitygraphicitemcontent.h"

#include <lineeditdelegate.h>
#include <listdelegate.h>

#include <QtGui/QGraphicsScene>

using namespace core;
using namespace gui;
using namespace swp;

SwpActivityGraphicItem::SwpActivityGraphicItem(const QString& name,
    QGraphicsItem* parent) : AbstractRectGraphicItem(parent),
    m_name(name.isEmpty() ? QObject::tr("activity") : name),
    m_nameProp(),
    m_inputProp(),
    m_outputProp(),
    m_actionsProp(),
    m_nameDelegate(0),
    m_actionsDelegate(0)
{
    QVariantList propsVal;
    propsVal << QObject::tr("none");

    m_nameDelegate = new LineEditDelegate();
    m_nameDelegate->setValidator(
        ValidatorManager::getInstance()->getValidator(
            "ActivityLabelValidator"));

    m_nameProp.Name = "name";
    m_nameProp.Text = QObject::tr("Activity name");
    m_nameProp.setValue(QVariant(m_name));
    m_nameProp.setDelegate(m_nameDelegate);

    m_inputProp.Name = "input";
    m_inputProp.Text = QObject::tr("Input");
    m_inputProp.setValue(propsVal);

    m_outputProp.Name = "output";
    m_outputProp.Text = QObject::tr("Output");
    m_outputProp.setValue(propsVal);

    m_actionsDelegate = new ListDelegate();

    m_actionsProp.Name = "actions";
    m_actionsProp.Text = QObject::tr("Actions");
    m_actionsProp.setValue("");
    m_actionsProp.setDelegate(m_actionsDelegate);

    QRectF rect(QPointF(0.0, 0.0), QPointF(100.0, 62.0));

    setRectF(rect);

    setFlags(flags() | QGraphicsItem::ItemIsMovable |
        QGraphicsItem::ItemIsSelectable);

    SwpActivityGraphicItemContent* _content =
            new SwpActivityGraphicItemContent("", this);
    _content->setActivityName(m_name);
    setContent(_content);

    setAntialiasing(false);

    setRadius(5);
}

SwpActivityGraphicItem::~SwpActivityGraphicItem()
{
    delete m_nameDelegate;
    delete m_actionsDelegate;
}

bool SwpActivityGraphicItem::supportsConnection(
    const ConnectionData& connection) const
{
    bool ret = false;

    if (connection.ConnectionId != CONNECTION_NOT_VALID
        && connection.From != this)
    {
        const int type = connection.From != 0 ? connection.From->type() :
            INVALID_TYPE;

        const QList<ConnectionData> _connections = connections();

        if ((connection.From == 0 ?
            outgoingConnectionSize(_connections) < 1 : true) &&
            !containsSameConnection(connection, _connections)) {
            ret = connection.From == 0 || ((type == SWP_START_POINT_ITEM ||
                type == SWP_ACTIVITY_ITEM || type == SWP_DECISION_ITEM ||
                type == SWP_JOIN_ITEM || type == SWP_SPLIT_ITEM ||
                type == SWP_MERGE_ITEM) &&
                    connection.ConnectionId == CONNECTION_ACTIVITY);
        }
    }

    return ret;
}

int SwpActivityGraphicItem::type() const
{
    return SWP_ACTIVITY_ITEM;
}

QString SwpActivityGraphicItem::name() const
{
    return QObject::tr("Activity");
}

IEditorItem* SwpActivityGraphicItem::copy() const
{
    return new SwpActivityGraphicItem(m_name, 0);
}

void SwpActivityGraphicItem::deserializationFinished(bool ok)
{
    AbstractRectGraphicItem::deserializationFinished(ok);
}

QList<Property> SwpActivityGraphicItem::properties() const
{
    QList<Property> ret;

    ret.append(m_nameProp);
    ret.append(m_inputProp);
    ret.append(m_outputProp);
    ret.append(m_actionsProp);

    return ret;
}

QVariant SwpActivityGraphicItem::propertyValue(
    const QString& propertyName) const
{
    QVariant ret;

    if (propertyName == m_nameProp.Name)
    {
        ret = m_nameProp.value();
    }
    else if (propertyName == m_inputProp.Name)
    {
        ret = m_inputProp.value();
    }
    else if (propertyName == m_outputProp.Name)
    {
        ret = m_outputProp.value();
    }
    else if (propertyName == m_actionsProp.Name)
    {
        ret = m_actionsProp.value();
    }

    return ret;
}

void SwpActivityGraphicItem::propertyChanged(const QString& property,
    const QVariant& newValue, bool silent)
{
    bool changed = false;

    if (property == m_nameProp.Name && m_nameProp.value() != newValue)
    {
        if (!silent)
        {
            PropertyChangeCommand* command = new PropertyChangeCommand(
                this, m_nameProp, newValue);

            UndoManager::getInstance()->push(
                dynamic_cast<IDocumentData*>(scene()), command);
        }

        m_nameProp.setValue(newValue);
        m_name = newValue.toString();

        SwpActivityGraphicItemContent* _content =
            dynamic_cast<SwpActivityGraphicItemContent*>(content());

        Q_ASSERT(_content != 0); // content mismatch

        _content->setActivityName(m_name);

        if (silent && isSelected()) {
            setSelected(false);
            setSelected(true);
        }

        changed = true;
    }
    else if (property == m_inputProp.Name &&
        m_inputProp.value().toList().first() != newValue)
    {
        QVariantList propsVal;
        propsVal << newValue;

        if (!silent)
        {
            PropertyChangeCommand* command = new PropertyChangeCommand(
                this, m_inputProp, newValue);

            UndoManager::getInstance()->push(
                dynamic_cast<IDocumentData*>(scene()), command);
        }

        m_inputProp.setValue(propsVal);

        if (silent && isSelected()) {
            setSelected(false);
            setSelected(true);
        }

        changed = true;
    }
    else if (property == m_outputProp.Name &&
        m_outputProp.value().toList().first() != newValue)
    {
        QVariantList propsVal;
        propsVal << newValue;

        if (!silent)
        {
            PropertyChangeCommand* command = new PropertyChangeCommand(
                this, m_outputProp, newValue);

            UndoManager::getInstance()->push(
                dynamic_cast<IDocumentData*>(scene()), command);
        }

        m_outputProp.setValue(propsVal);

        if (silent && isSelected()) {
            setSelected(false);
            setSelected(true);
        }

        changed = true;
    }
    else if (property == m_actionsProp.Name &&
        m_actionsProp.value() != newValue)
    {
        if (!silent)
        {
            PropertyChangeCommand* command = new PropertyChangeCommand(
                this, m_actionsProp, newValue);

            UndoManager::getInstance()->push(
                dynamic_cast<IDocumentData*>(scene()), command);
        }

        m_actionsProp.setValue(newValue);

        if (silent && isSelected()) {
            setSelected(false);
            setSelected(true);
        }

        changed = true;
    }

    if (silent && changed)
    {
        GraphicData* data = dynamic_cast<GraphicData*>(m_documentData);

        Q_ASSERT(data != 0); // shouldn't be null

        data->emitDataChanged();
    }
}

QVariantList SwpActivityGraphicItem::optionList(
    const QString& propertyName) const
{
    QVariantList ret;

    if (propertyName == m_inputProp.Name)
    {
        ret << QObject::tr("none");

        ProcessDocumentAutocomplete* ac =
            ProcessDocumentAutocomplete::getInstance();

        const QList<QString> inputs = ac->getInputs(m_documentData);

        foreach (const QString& input, inputs)
        {
            ret.append(input);
        }
    }
    else if (propertyName == m_outputProp.Name)
    {
        ret << QObject::tr("none");

        ProcessDocumentAutocomplete* ac =
            ProcessDocumentAutocomplete::getInstance();

        const QList<QString> outputs = ac->getOutputs(m_documentData);

        foreach (const QString& output, outputs)
        {
            ret.append(output);
        }
    }

    return ret;
}

QVariantList SwpActivityGraphicItem::autocomplete(
    const QString& propertyName) const
{
    QVariantList ret;

    return ret;
}

QVariant SwpActivityGraphicItem::itemChange(GraphicsItemChange change,
    const QVariant &value)
{
    return AbstractRectGraphicItem::itemChange(change, value);
}

QList<QPointF> SwpActivityGraphicItem::scalePoints() const
{
    QList<QPointF> ret;

    ret << QPointF(50.0, 0.0) << QPointF(50.0, 62.0)
        << QPointF(0.0, 31.0) << QPointF(100.0, 31.0);

    return ret;
}

QList<QPointF> SwpActivityGraphicItem::connectPoints() const
{
    QList<QPointF> ret;

    ret << QPointF(0.0, 0.0) << QPointF(50.0, 0.0) <<
           QPointF(100.0, 0.0) << QPointF(100.0, 31.0) <<
           QPointF(100.0, 62.0) << QPointF(50.0, 62.0) <<
           QPointF(0.0, 62.0) << QPointF(0.0, 31.0);

    return ret;
}

QList<QPointF> SwpActivityGraphicItem::selfConnectAnchorPoints() const
{
    QList<QPointF> ret;

    ret << QPointF(-20.0, -20.0) << QPointF(50.0, -20.0) <<
           QPointF(120.0, -20.0) << QPointF(120.0, 31.0) <<
           QPointF(120.0, 82.0) << QPointF(50.0, 82.0) <<
           QPointF(-20.0, 82.0) << QPointF(-20.0, 31.0);

    return ret;
}

bool SwpActivityGraphicItem::containsSameConnection(
    const ConnectionData& connection,
    const QList<ConnectionData>& connections) const
{
    bool ret = false;

    foreach (const ConnectionData& _connection, connections)
    {
        if (ConnectionComparator::compare(connection, _connection,
            ConnectionComparator::WithoutConnector))
        {
            ret = true;
            break;
        }
    }

    return ret;
}

int SwpActivityGraphicItem::outgoingConnectionSize(
    const QList<ConnectionData>& connections) const
{
    int ret = 0;

    foreach (const ConnectionData& _connection, connections)
    {
        if (_connection.From == this)
        {
            ++ret;
        }
    }

    return ret;
}
